以下代碼是Emit版本,我把C#對應IL部分都寫在註解。
public static class DemoExtension
{
	public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql) where T : new()
	{
		using (var command = cnn.CreateCommand())
		{
			command.CommandText = sql;
			using (var reader = command.ExecuteReader())
			{
				var func = GetTypeDeserializerImpl(typeof(T), reader);
				while (reader.Read())
				{
					var result = func(reader as DbDataReader);
					yield return result is T ? (T)result : default(T);
				}
			}
		}
	}
	private static Func<DbDataReader, object> GetTypeDeserializerImpl(Type type, IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false)
	{
		var returnType = type.IsValueType ? typeof(object) : type;
		var dm = new DynamicMethod("Deserialize" + Guid.NewGuid().ToString(), returnType, new[] { typeof(IDataReader) }, type, true);
		var il = dm.GetILGenerator();
		//C# : User user = new User();
		//IL : 
		//IL_0001:  newobj      
		//IL_0006:  stloc.0         
		var constructor = returnType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)[0]; //這邊簡化成只會有預設constructor
		il.Emit(OpCodes.Newobj, constructor);
		var returnValueLocal = il.DeclareLocal(type);
		il.Emit(OpCodes.Stloc, returnValueLocal); //User user = new User();
		// C# : 
		//object value = default(object);
		// IL :
		//IL_0007: ldnull
		//IL_0008:  stloc.1     // value	
		var valueLoacl = il.DeclareLocal(typeof(object));
		il.Emit(OpCodes.Ldnull);
		il.Emit(OpCodes.Stloc, valueLoacl);
		
		
		int index = startBound;
		var getItem = typeof(IDataRecord).GetProperties(BindingFlags.Instance | BindingFlags.Public)
						.Where(p => p.GetIndexParameters().Length > 0 && p.GetIndexParameters()[0].ParameterType == typeof(int))
						.Select(p => p.GetGetMethod()).First();
		foreach (var p in type.GetProperties())
		{
			//C# : value = P_0[0];
			//IL:
			//IL_0009:  ldarg.0			
			//IL_000A: ldc.i4.0
			//IL_000B: callvirt System.Data.IDataRecord.get_Item
			//IL_0010:  stloc.1     // value				
			il.Emit(OpCodes.Ldarg_0); //取得reader參數
			EmitInt32(il, index);
			il.Emit(OpCodes.Callvirt, getItem);
			il.Emit(OpCodes.Stloc, valueLoacl);
			//C#: if (!(value is DBNull)) user.Name = (string)value;
			//IL:
			// IL_0011:  ldloc.1     // value
			// IL_0012:  isinst      System.DBNull
			// IL_0017:  ldnull      
			// IL_0018:  cgt.un      
			// IL_001A:  ldc.i4.0   
			// IL_001B:  ceq         
			// IL_001D:  stloc.2    
			// IL_001E:  ldloc.2     
			// IL_001F:  brfalse.s   IL_002E
			// IL_0021:  ldloc.0     // user
			// IL_0022:  ldloc.1     // value
			// IL_0023:  castclass   System.String
			// IL_0028:  callvirt    UserQuery+User.set_Name			
			il.Emit(OpCodes.Ldloc, valueLoacl);
			il.Emit(OpCodes.Isinst, typeof(System.DBNull));
			il.Emit(OpCodes.Ldnull);
			
			var tmpLoacl = il.DeclareLocal(typeof(int));
			il.Emit(OpCodes.Cgt_Un);
			il.Emit(OpCodes.Ldc_I4_0);
			il.Emit(OpCodes.Ceq);
			
			il.Emit(OpCodes.Stloc,tmpLoacl);
			il.Emit(OpCodes.Ldloc,tmpLoacl);
			
			
			var labelFalse = il.DefineLabel();
			il.Emit(OpCodes.Brfalse_S,labelFalse);
			il.Emit(OpCodes.Ldloc, returnValueLocal);
			il.Emit(OpCodes.Ldloc, valueLoacl);
			if (p.PropertyType.IsValueType)
				il.Emit(OpCodes.Unbox_Any, p.PropertyType);
			else
				il.Emit(OpCodes.Castclass, p.PropertyType);
			il.Emit(OpCodes.Callvirt, p.SetMethod);
			
			il.MarkLabel(labelFalse);
			index++;
		}
		// IL_0053:  ldloc.0     // user
		// IL_0054:  stloc.s     04  //不需要
		// IL_0056:  br.s        IL_0058
		// IL_0058:  ldloc.s     04  //不需要
		// IL_005A:  ret         
		il.Emit(OpCodes.Ldloc, returnValueLocal);
		il.Emit(OpCodes.Ret);
		var funcType = System.Linq.Expressions.Expression.GetFuncType(typeof(IDataReader), returnType);
		return (Func<IDataReader, object>)dm.CreateDelegate(funcType);
	}
	private static void EmitInt32(ILGenerator il, int value)
	{
		switch (value)
		{
			case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
			case 0: il.Emit(OpCodes.Ldc_I4_0); break;
			case 1: il.Emit(OpCodes.Ldc_I4_1); break;
			case 2: il.Emit(OpCodes.Ldc_I4_2); break;
			case 3: il.Emit(OpCodes.Ldc_I4_3); break;
			case 4: il.Emit(OpCodes.Ldc_I4_4); break;
			case 5: il.Emit(OpCodes.Ldc_I4_5); break;
			case 6: il.Emit(OpCodes.Ldc_I4_6); break;
			case 7: il.Emit(OpCodes.Ldc_I4_7); break;
			case 8: il.Emit(OpCodes.Ldc_I4_8); break;
			default:
				if (value >= -128 && value <= 127)
				{
					il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
				}
				else
				{
					il.Emit(OpCodes.Ldc_I4, value);
				}
				break;
		}
	}
}
這邊Emit的細節概念非常的多,這邊無法全部都講解,先挑出重要概念講解
在Emit if/else需要使用Label定位,告知編譯器條件為true/false時要跳到哪個位子,舉例 : 「boolean轉整數」,假設要簡單將Boolean轉換成Int,C#代碼可以用「如果是True返回1否則返回0」邏輯來寫:
	public static int BoolToInt(bool input) => input ? 1 : 0;
當轉成Emit寫法的時候,需要以下邏輯 :
(注意,這時候Label位子還沒確定)
符合條件要運行區塊的前一行,使用MarkLabel方法標記Label的位子。最後寫出的C# Emit代碼 :
public class Program
{
	public static void Main(string[] args)
	{
		var func = CreateFunc();
		Console.WriteLine(func(true)); //1
		Console.WriteLine(func(false)); //0
	}
	static Func<bool, int> CreateFunc()
	{
		var dm = new DynamicMethod("Test" + Guid.NewGuid().ToString(), typeof(int), new[] { typeof(bool) });
		var il = dm.GetILGenerator();
		var labelTrue = il.DefineLabel();
		il.Emit(OpCodes.Ldarg_0);
		il.Emit(OpCodes.Brtrue_S, labelTrue);
		il.Emit(OpCodes.Ldc_I4_0);
		il.Emit(OpCodes.Ret);
		il.MarkLabel(labelTrue);
		il.Emit(OpCodes.Ldc_I4_1);
		il.Emit(OpCodes.Ret);
		var funcType = System.Linq.Expressions.Expression.GetFuncType(typeof(bool), typeof(int));
		return (Func<bool, int>)dm.CreateDelegate(funcType);
	}
}
這邊可以發現Emit版本
優點 :
缺點 :
接著來看Dapper作者的建議,現在一般專案當中沒有必要使用Emit,使用Expression + Func/Action已經可以解決大部分動態方法的需求,尤其是Expression支援Block等方法情況。連結 c# - What's faster: expression trees or manually emitting IL

話雖如此,但有一些厲害的開源專案就是使用Emit管理細節,如果想看懂它們,就需要基礎的Emit IL概念。